
#include "macros.hpp"
#include "SplineR2.hpp"
#include "Parametrization.hpp"
#include "CLParametrization.hpp"
#include "CentripetalParametrization.hpp"
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdlib>
using namespace std;

enum ParamType { UNIFORM, CHORD_LENGTH, CENTRIPETAL };

struct Input
{
	vector<Spline::Point> control_points;
	uint samples;
	Spline::Point click_point, move_point;
};

void usage( char* p )
{
	cerr << "Uso: " << p << " [input] [output]" << endl;
}

Input read_input( char* file )
{
	ifstream f( file );
	Input input;
	uint n;

	// lee la primera línea (puntos, samples)
	f >> n >> input.samples;

	// lee los puntos de control
	forn(i,n-1)
	{
		double x, y;
		f >> x >> y;
		input.control_points.push_back( Spline::Point(x,y) );
	}

	// obtiene el punto "clickeado"
	f >> input.click_point.x >> input.click_point.y;

	// obtiene el punto a donde se mueve
	f >> input.move_point.x >> input.move_point.y;

	f.close();

	return input;
}

ParamType getParametrization( int argc, char** argv )
{
	ParamType parametrization = UNIFORM;
	if( argc >= 5 && argv[3][0] == '-' && argv[3][1] == 'p' )
		parametrization = (ParamType)atoi(argv[4]);
	return parametrization;
}

int main( int argc, char** argv )
{
	if( argc < 3 )
	{
		usage(argv[0]);
		return 1;
	}

	Input input = read_input(argv[1]);
	
	Parametrization* parametrization;
	switch(	getParametrization(argc,argv) )
	{
		case CHORD_LENGTH: 
			parametrization = new CLParametrization( input.control_points );
			break;
		case CENTRIPETAL:
			parametrization = new CentripetalParametrization( input.control_points );
			break;
		default:
			parametrization = new UniformParametrization( input.control_points );
			break;
	}

	SplineR2 spline( input.control_points, *parametrization );

	delete parametrization;

	// Obtiene el punto más cercano al "clickeado"
	double nearest_t = spline.nearest( input.click_point );
	Spline::Point nearest = spline( nearest_t );

	// Obtiene la muestra de puntos del spline
	vector<Spline::Point> sample = spline.sample( input.samples );

	// Obtiene la muestra de puntos del spline modificado
	spline.movePoint( nearest_t, input.move_point );
	vector<Spline::Point> mod_sample = spline.sample( input.samples );

	// Guarda el muestreo del spline
	ofstream output(argv[2]);
	output << sample.size() << endl;
	foreach(point, sample)
		output << point->x << ' ' << point->y << endl;
	output << endl;
	output << nearest.x << ' ' << nearest.y << endl;
	output << endl;

	// Guarda el muestreo del spline modificado
	foreach(point, mod_sample)
		output << point->x << ' ' << point->y << endl;
	output.close();
	
	return 0;
}

